/* ============================================================
 * Part of 'Autostop' project
 * Under GPL v2 license - http://www.gnu.org/licenses/gpl.html
 * (c) 2006 - Autostop team
 */

///\file fakecamera.h Qt-based, fake camera driver lib. Uses libcamera interface, supplies images from disk
/// Files are read in alphabetical order from directory, which name is read from
/// AUTOSTOP_FAKEFILES_PATH environment variable. PNG files are used as camre input, JPG2K files
/// are used to create sequence. Images from camera are supplied are fake rate of 20fps
/// Files are read on camera_init, and then supplied from memory-buffer.

#include <stdlib.h>
#include <sys/time.h>	// gettimeofday
#include <unistd.h>

#include <QList>
#include <QFileInfo>
#include <QDir>
#include <QImage>

#include "camera.h"

// =================================
// constants
#define ENV_FILES_PATH	"AUTOSTOP_FAKEFILES_PATH"	///< env variable contaning the path to files
#define FALLBACK_PATH	"/autostop/fakeimages"		///< fallback path
#define ENV_SEGMENTED_PATH "AUTOSTOP_FAKESEGMENTED_PATH" ///< env var for fake segmented path
#define FALLBACK_SEGMENTED_PATH "/autostop/fakesegmented"

// =================================
// globals
QList<QFileInfo>	Images;					///< Camera images
QList<QFileInfo>	Segmented;				///< Camera images
QList<QFileInfo>	Sequence;				///< Sequence images (jp2)
QList<QFileInfo>::iterator	ImagesIterator;	///< Current image read from camera
QList<QFileInfo>::iterator	SegmentedIterator;

QString				ErrorMsg	= "No error";	///< Error message buffer

int jpeg_size = 8;		///< locally stored jpeg frame size

// ========================================================
int camera_init(void)
{
	// get path
	const char* path = getenv( ENV_FILES_PATH );
	if ( path == 0 )
	{
		path = FALLBACK_PATH;
	}
	// read images
	QDir dir( path );

	//check
	if ( ! dir.exists() )
	{
		//qDebug( "directory %s does not exists!", path );
		ErrorMsg = QString( "directory %1 does not exists!" ).arg( path );
		return CAMERA_ERROR;
	}
	
	// get segmented dir
	const char* segmentedPath = getenv( ENV_SEGMENTED_PATH );
	if ( segmentedPath == 0 )
	{
		segmentedPath = FALLBACK_SEGMENTED_PATH;
	}
	// read images
	QDir dirSegmented( segmentedPath );

	//check
	if ( ! dirSegmented.exists() )
	{
		ErrorMsg = QString( "directory %1 does not exists!" ).arg( segmentedPath );
		return CAMERA_ERROR;
	}
	

	// prepare name filters
	QStringList imageFilters, sequenceFilters;
	imageFilters << "*.png" << "*.PNG";
	sequenceFilters << "*.jp2" << "*.JP2";

	// get image list
	Images		= dir.entryInfoList( imageFilters );
	Segmented	= dirSegmented.entryInfoList( imageFilters );
	Sequence	= dir.entryInfoList( sequenceFilters );
	
	
	// check image list
	if ( Images.count() == 0 )
	{
		//qDebug("No images in %s", path );
		ErrorMsg = QString( "No images in %1" ).arg( path );
		return CAMERA_ERROR;
	}

	// reset iterator
	ImagesIterator = Images.begin();
	SegmentedIterator = Segmented.begin();
	
	return CAMERA_OK;
}

// ========================================================
uint64_t camera_get_time()
{
	struct timeval t;
	gettimeofday( & t, NULL );
	return (uint64_t)t.tv_sec * 1000 + t.tv_usec / 1000;
}

// ========================================================
int camera_image_width(void)
{
	return 640;
}

// ========================================================
int camera_image_height(void)
{
	return 480;
}

// ========================================================
void set_timestamp_to_current( uint64_t* ptimestamp )
{
	if ( ptimestamp )
	{
		struct timeval tv;
		gettimeofday( &tv, NULL );
		
		*ptimestamp = tv.tv_sec * 1000 + tv.tv_usec / 1000;
	}
}

// ========================================================
int camera_get_image( uint8_t* pbuffer, size_t bufsize, uint64_t* ptimestamp )
{
	set_timestamp_to_current( ptimestamp );
	
	if ( ImagesIterator != Images.end() )
	{
		QFileInfo fileInfo = *ImagesIterator;
		// read from file
		QImage image;
		if ( ! image.load( fileInfo.absoluteFilePath() ) )
		{
			ErrorMsg = "Could not read image";
			return CAMERA_ERROR;
		}
		
		//qDebug("Supplying frame from image: %s", qPrintable( fileInfo.baseName() ) );
		
		// copy to buffer
		uint8_t* p = pbuffer;
		int w = camera_image_width();
		int h = camera_image_height();
		
		for( int y = 0; y < h; y++ )
		{
			for( int x = 0; x < w; x++ )
			{
				// check buffer size
				if ( p < pbuffer + bufsize )
				{
					// check imgae size vs declared image size
					if ( y < image.height() && x < image.width() )
					{
						QRgb pixel = image.pixel( x, y );
						*p = qGray( pixel );
					}
					else
					{
						*p = 0;
					}
					
					p++;
				}
				else
				{
					break;
				}
			}// x
		}// y
		
		// get next image
		ImagesIterator++;
		
		// keep sequence
		if( ImagesIterator == Images.end() ) ImagesIterator = Images.begin();
		
		return CAMERA_OK;
	}
	ErrorMsg = "Fake camera: out of images";
	return CAMERA_EOF;
}

// ========================================================
sequence_t camera_open_sequence(void)
{
	return SEQUENCE_INVALID;
}

// ========================================================
int camera_read_sequence( sequence_t sequence, uint8_t* pbuffer, size_t bufsize )
{
	Q_UNUSED( sequence );
	Q_UNUSED( pbuffer );
	Q_UNUSED( bufsize );
	
	return CAMERA_ERROR;
}

// ========================================================
void camera_close_sequence( sequence_t sequence )
{
	Q_UNUSED( sequence );
	// nothing
}

// ========================================================
const char* camera_error_message()
{
	return qPrintable(ErrorMsg);
}

// ========================================================
int camera_get_segmented_image( uint8_t* pbuffer, size_t bufsize, uint64_t* ptimestamp )
{
	set_timestamp_to_current( ptimestamp );
	
	if ( SegmentedIterator != Segmented.end() )
	{
		QFileInfo fileInfo = *SegmentedIterator;
		// read from file
		QImage image;
		if ( ! image.load( fileInfo.absoluteFilePath() ) )
		{
			ErrorMsg = "Could not read segmentd image";
			return CAMERA_ERROR;
		}
		
		// copy to buffer
		uint8_t* p = pbuffer;
		int w = image.width();
		int h = image.height();
		
		for( int y = 0; y < h; y++ )
		{
			for( int x = 0; x < w; x++ )
			{
				// check buffer size
				if ( p < pbuffer + bufsize )
				{
					// check imgae size vs declared image size
					if ( y < image.height() && x < image.width() )
					{
						QRgb pixel = image.pixel( x, y );
						*p = qGray( pixel );
					}
					else
					{
						*p = 0;
					}
					
					p++;
				}
				else
				{
					break;
				}
			}// x
		}// y
		
		// get next image
		SegmentedIterator++;
		
		// keep sequence
		if( SegmentedIterator == Segmented.end() )
		{
			SegmentedIterator = Segmented.begin();
		}
		
	}
	
	// sleep like 250 ms
	usleep( 250*1000 );
	
	return CAMERA_OK;
}

// ========================================================
int camera_set_bgfactor( uint16_t bgfactor )
{
	return CAMERA_OK; // do nothing
}

// ========================================================
int camera_set_fgtreshold( uint8_t fgtreshold  )
{
	return CAMERA_OK; // do nothing
}

// ========================================================
int camera_get_jpeg( uint8_t* pbuffer, size_t bufsize, size_t* imagesize, uint64_t* timestamp  )
{
	QFile file( ":/res/image.j2k" );
	if ( ! file.open( QIODevice::ReadOnly ) )
	{
		ErrorMsg = "Could not open internal jpeg file";
		return CAMERA_ERROR;
	}
	
	if ( timestamp )
	{
		*timestamp = camera_get_time();
	}
	
	if ( file.size() > bufsize )
	{
		ErrorMsg = "Jpeg2k buffer too small";
		return CAMERA_SMALLBUFFER;
	}
	
	*imagesize = file.read( (char*)pbuffer, file.size() );
	
	return CAMERA_OK;
}
// ========================================================
int camera_jpeg_set_shrink( int shrink )
{
	return CAMERA_OK;
}

// ========================================================
int camera_jpeg_set_quality( int size )
{
	jpeg_size = size;
	return CAMERA_OK;
}

// ========================================================
int camera_get_jpeg_quality()
{
	return jpeg_size;
}

// ========================================================
int camera_set_exposure( int exposure )
{
	return CAMERA_OK;
}

// ========================================================
int camera_set_auto_exposure( int auto_exposure )
{
	return CAMERA_OK;
}

// ========================================================
double camera_accel_get_x()
{
	return 0.5;
}

// ========================================================
double camera_accel_get_y()
{
	return 0.5;
}

// eof

